package com.workingpub.commons.datasource.internal;

import java.util.Collection;
import java.util.Dictionary;
import java.util.Hashtable;

import javax.sql.DataSource;
import javax.sql.XADataSource;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.workingpub.commons.datasource.ConfigProperties;
import com.workingpub.commons.datasource.DataSourceConstants;
import com.workingpub.commons.datasource.DataSourceGenerator;
import com.workingpub.commons.datasource.XADataSourceBuilder;

public class DefaultDataSourceGenerator implements DataSourceGenerator {
	private static final Logger logger = LoggerFactory.getLogger(DefaultDataSourceGenerator.class);
	private BundleContext bundleContext;

	public DefaultDataSourceGenerator() {
		Bundle bundle = FrameworkUtil.getBundle(getClass());
		Assert.notNull(bundle, "Get the bundle instance is null.");
		this.bundleContext = bundle.getBundleContext();
		Assert.notNull(bundleContext, "Get the bundle context is null.");
	}

	@Override
	public DataSource generate(ConfigProperties config) {
		Assert.notNull(config, "Failed to generated datasource : config properties instance is null.");
		Assert.isTrue(StringUtils.hasText(config.getDomain()),
				"Failed to generated datasource : domain string is null or empty.");
		DataSource dataSource = findByDomain(config);
		if (null == dataSource) {
			buildXADataSource(config);
		}
		dataSource = findByDomain(config);
		Assert.notNull(dataSource, "Build xa datasource result is null : " + config);

		return dataSource;
	}

	private DataSource findByDomain(ConfigProperties config) {
		String filter = "(osgi.jndi.service.name=" + DataSourceConstants.PREFIX_XA_JNDI + config.getDomain() + ")";
		try {
			Collection<ServiceReference<DataSource>> references = bundleContext.getServiceReferences(DataSource.class,
					filter);
			if (null == references || references.isEmpty()) {
				return null;
			}
			for (ServiceReference<DataSource> entry : references) {
				return bundleContext.getService(entry);
			}
		} catch (InvalidSyntaxException e) {
			logger.warn("Failed to found xa datasource service bean : " + e.getMessage() + "\n" + config, e);
			throw new IllegalArgumentException(e);
		}
		return null;
	}

	private XADataSource buildXADataSource(ConfigProperties config) {
		try {
			Collection<ServiceReference<XADataSourceBuilder>> references = bundleContext
					.getServiceReferences(XADataSourceBuilder.class, null);
			Assert.notEmpty(references, "Didn't find the service bean to build xa datasource.");
			for (ServiceReference<XADataSourceBuilder> entry : references) {
				XADataSourceBuilder builder = bundleContext.getService(entry);
				if (config.getDriverClass().equalsIgnoreCase(builder.getDataSourceTypeName())) {
					XADataSource xaDataSource = builder.build(config);

					Dictionary<String, String> properties = new Hashtable<String, String>();
					properties.put(DataSourceConstants.Property_XA_JNDI,
							DataSourceConstants.PREFIX_XA_JNDI + config.getDomain());
					properties.put(DataSourceConstants.Property_XA_JTA, DataSourceConstants.Global_XAJTA_Name);
					properties.put("aries.xa.username", config.getJdbcUser());
					properties.put("aries.xa.password", config.getJdbcPassword());
					properties.put("aries.xa.poolMaxSize",
							(null == config.getMaxPoolSize() || config.getMaxPoolSize() <= 0) ? Default_Pool_Max_Size
									: config.getMaxPoolSize().toString());
					properties.put("aries.xa.poolMinSize",
							(null == config.getMinPoolSize() || config.getMinPoolSize() <= 0) ? Default_Pool_Min_Size
									: config.getMinPoolSize().toString());
					properties.put("aries.xa.allConnectionsEquals", config.getAllConnectionsEquals().toString());
					properties.put("aries.xa.connectionMadIdleMinutes",
							config.getConnectionMadIdleMinutes().toString());
					properties.put("aries.xa.connectionMaxWaitMilliseconds",
							config.getConnectionMaxWaitMilliseconds().toString());
					properties.put(DataSourceConstants.Property_DatabaseType, builder.getDatabaseType().name());
					bundleContext.registerService(XADataSource.class, xaDataSource, properties);
					config.setDatabaseType(builder.getDatabaseType());
					return xaDataSource;
				}
			}
		} catch (InvalidSyntaxException e) {
			Assert.isNull(e, "Failed to builder xa datasource : " + e.getMessage() + "\n" + config);
		}
		return null;
	}
}
